//
// CustomAttribute.cs
//
// Author:
//   Jb Evain (jbevain@gmail.com)
//
// (C) 2005 Jb Evain
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Mono.Cecil {

	public struct CustomAttributeArgument {

		readonly TypeReference type;
		readonly object value;

		public TypeReference Type {
			get { return type; }
		}

		public object Value {
			get { return value; }
		}

		public CustomAttributeArgument (TypeReference type, object value)
		{
			this.type = type;
			this.value = value;
		}
	}

	public sealed class CustomAttributeArgumentCollection : Collection<CustomAttributeArgument> {}

	public sealed class CustomAttribute {

		uint signature;
		bool resolved;
		MethodReference constructor;
		internal CustomAttributeArgumentCollection arguments;
		internal Dictionary<string, CustomAttributeArgument> fields;
		internal Dictionary<string, CustomAttributeArgument> properties;

		public MethodReference Constructor {
			get { return constructor; }
			set { constructor = value; }
		}

		public bool HasConstructorArguments {
			get {
				Resolve ();

				return arguments != null && arguments.Count > 0;
			}
		}

		public CustomAttributeArgumentCollection ConstructorArguments {
			get {
				Resolve ();

				if (arguments == null)
					arguments = new CustomAttributeArgumentCollection ();

				return arguments;
			}
		}

		public bool HasFields {
			get {
				Resolve ();

				return fields != null && fields.Count > 0;
			}
		}

		public IDictionary<string, CustomAttributeArgument> Fields {
			get {
				Resolve ();

				if (fields == null)
					fields = new Dictionary<string, CustomAttributeArgument> ();

				return fields;
			}
		}

		public bool HasProperties {
			get {
				Resolve ();

				return properties != null && properties.Count > 0;
			}
		}

		public IDictionary<string, CustomAttributeArgument> Properties {
			get {
				Resolve ();

				if (properties == null)
					properties = new Dictionary<string, CustomAttributeArgument> ();

				return properties;
			}
		}

		public byte [] Blob {
			get {
				if (!HasImage)
					throw new NotSupportedException ();

				return Module.Read (this, (attribute, reader) => reader.ReadCustomAttributeBlob (attribute.signature)); ;
			}
		}

		internal bool HasImage {
			get { return constructor != null && constructor.HasImage; }
		}

		internal ModuleDefinition Module {
			get { return constructor.DeclaringType.Module; }
		}

		public bool IsResolved {
			get { return resolved; }
		}

		internal CustomAttribute (uint signature, MethodReference constructor)
		{
			this.signature = signature;
			this.constructor = constructor;
			this.resolved = false;
		}

		public CustomAttribute (MethodReference constructor)
		{
			this.constructor = constructor;
			this.resolved = true;
		}

		public void Resolve ()
		{
			if (resolved || !HasImage)
				return;

			Module.Read (this, (attribute, reader) => {
				reader.ReadCustomAttributeSignature (attribute.signature, attribute);
				return this;
			});

			resolved = true;
		}
	}
}
